package com.hurlant.eval
{
	import flash.events.IOErrorEvent;
	import flash.net.URLLoader;
	import flash.utils.ByteArray;
	import flash.utils.getDefinitionByName;
	
	/**
	 * So you don't want to back-port changes in ESC to AS3 every other day?
	 * And yet you want to reap the benefits of ESC's updates?
	 * 
	 * Ok. Go with binary. 
	 * 
	 */
	public class CompiledESC
	{
		private static var done:Boolean = false;
		public function CompiledESC() {
			if (!done) {
				loadESC();
				done = true;
			}
		}
		
		[Embed(source="../../../abc/debug.es.abc",mimeType="application/octet-stream")]
        private var debug_abc:Class;
		[Embed(source="../../../abc/util.es.abc",mimeType="application/octet-stream")]
        private var util_abc:Class;
		[Embed(source="../../../abc/bytes-tamarin.es.abc",mimeType="application/octet-stream")]
        private var bytes_tamarin_abc:Class;
		[Embed(source="../../../abc/util-tamarin.es.abc",mimeType="application/octet-stream")]
        private var util_tamarin_abc:Class;
		[Embed(source="../../../abc/lex-char.es.abc",mimeType="application/octet-stream")]
        private var lex_char_abc:Class;
		[Embed(source="../../../abc/lex-scan.es.abc",mimeType="application/octet-stream")]
        private var lex_scan_abc:Class;
		[Embed(source="../../../abc/lex-token.es.abc",mimeType="application/octet-stream")]
        private var lex_token_abc:Class;
		[Embed(source="../../../abc/ast.es.abc",mimeType="application/octet-stream")]
        private var ast_abc:Class;
		[Embed(source="../../../abc/parse.es.abc",mimeType="application/octet-stream")]
        private var parse_abc:Class;
		[Embed(source="../../../abc/asm.es.abc",mimeType="application/octet-stream")]
        private var asm_abc:Class;
		[Embed(source="../../../abc/abc.es.abc",mimeType="application/octet-stream")]
        private var abc_abc:Class;
		[Embed(source="../../../abc/emit.es.abc",mimeType="application/octet-stream")]
        private var emit_abc:Class;
		[Embed(source="../../../abc/cogen.es.abc",mimeType="application/octet-stream")]
        private var cogen_abc:Class;
		[Embed(source="../../../abc/cogen-stmt.es.abc",mimeType="application/octet-stream")]
        private var cogen_stmt_abc:Class;
		[Embed(source="../../../abc/cogen-expr.es.abc",mimeType="application/octet-stream")]
        private var cogen_expr_abc:Class;
		[Embed(source="../../../abc/esc-core.es.abc",mimeType="application/octet-stream")]
        private var esc_core_abc:Class;
		[Embed(source="../../../abc/eval-support.es.abc",mimeType="application/octet-stream")]
        private var eval_support_abc:Class;
		[Embed(source="../../../abc/esc-env.es.abc",mimeType="application/octet-stream")]
        private var esc_env_abc:Class;
        
		/**
		 * New approach: load all ABCs in one big SWF.
		 * it guarantees ordering and is probably more efficient.
		 * (historically, this also minimized the chance of loaders getting GC'd before completing,
		 *  but we just keep loaders around forever now. see ByteLoader.)
		 */
		private function loadESC():void {
			var a:Array = [
				new debug_abc as ByteArray,
				new util_abc as ByteArray,
				new bytes_tamarin_abc as ByteArray,
				new util_tamarin_abc as ByteArray,
				new lex_char_abc as ByteArray,
				new lex_scan_abc as ByteArray,
				new lex_token_abc as ByteArray,
				new ast_abc as ByteArray,
				new parse_abc as ByteArray,
				new asm_abc as ByteArray,
				new abc_abc as ByteArray,
				new emit_abc as ByteArray,
				new cogen_abc as ByteArray,
				new cogen_stmt_abc as ByteArray,
				new cogen_expr_abc as ByteArray,
				new esc_core_abc as ByteArray,
				//new eval_support_abc as ByteArray,
				new esc_env_abc as ByteArray,
			]
			ByteLoader.loadBytes(a, true);
		}
		
		public function compile(str:String, name:String=null):ByteArray {
			var compile:Function = getDefinitionByName("ESC::compile") as Function;
			var array:ByteArray;
			if (name==null) name="[eval]"; //str;
			compile( function():String { return str },
					 function(abc:*):void { array = abc.getBytes() },
					 name );
			array.position = 0;
			return array;
		}

		// stoken from eval-support.es
		private var eval_counter:int=0;
		public static var scopes:Array = [];
		public static var cb:Array = [];
		public function evaluateInScopeArray(script:String, scopes:Array=null, callback:Function = null):* {
        	// import some functions and objects
			var getTopFixtures:Function = getDefinitionByName("ESC::getTopFixtures") as Function;
			var Parser:Class = getDefinitionByName("Parse::Parser") as Class;
			var MethodFixture:Class = getDefinitionByName("Ast::MethodFixture") as Class;
			var ValFixture:Class = getDefinitionByName("Ast::ValFixture") as Class;
			var syntaxError:Function = getDefinitionByName("Util::syntaxError") as Function;
			var cgEval:Function = getDefinitionByName("Gen::cgEval") as Function;
			 
            var id:int = eval_counter++;
            var fixtures:* = getTopFixtures();
            var file:String = "(EVAL CODE)";
            var name:String = "$eval$" + id + "$";

			// gross scope hack, only way I know how :(..
			if (scopes!=null) {
				var hasScope:Boolean = false;
	            for (var i:int=0;i<scopes.length;i++) {
	            	var n:int = CompiledESC.scopes.length;
	            	CompiledESC.scopes[n] = scopes[i];
	            	script = "with(CompiledESC.scopes["+n+"]){\n"+script+"\n}\ndelete CompiledESC.scopes["+n+"];\n"; // scope and clean up...
	            	hasScope = true;
	            }
	            if (hasScope) {
	            	script = "namespace _com_hurlant_eval = 'com.hurlant.eval';\n" +
			            	 "use namespace _com_hurlant_eval;\n" +
			            	 script;
	            }
	  		}
	  		script = "namespace _flash_utils = 'flash.utils';\n" +
			         "use namespace _flash_utils;\n" +
			         script;
 
            var parser:* = new Parser( script, getTopFixtures(), file );
            var prog:* = parser.program();

            var b:ByteArray = cgEval(prog, name, "").getBytes();
            // time for some kewl gross scope hack, since the crap here doesn't speak to me.
            var runme:String
            if (callback!=null) {
            	n = CompiledESC.cb.length;
            	CompiledESC.cb[n] = callback;
            	runme = "namespace com_hurlant_eval = 'com.hurlant.eval';\n" +
            			"use namespace com_hurlant_eval;\n" + 
		            	"namespace _flash_utils = 'flash.utils';\n" +
		            	"use namespace _flash_utils;\n" +
            			"CompiledESC.cb["+n+"](ESC::eval_hook());delete CompiledESC.cb["+n+"];\n";
            } else {
            	runme = "ESC::eval_hook()";
            }
            var b2:ByteArray = compile(runme);
            ByteLoader.loadBytes( [b, b2], true );
            
            // this is too soon. no return value for you!
            return; // eval_hook(scopes);
		}
	}
}